using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

namespace Elte.SqlSpIdx
{
    public partial class VectorOperations
    {
        private static readonly Random rnd = new Random();

        public static double DotProd(double[] vv1, double[] vv2)
        {
            double res = 0;
            for (int i = 0; i < vv1.Length; i++)
                res += vv1[i] * vv2[i];

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlDouble VectorDotProd(SqlBinary v1, SqlBinary v2)
        {
            double[] vv1, vv2;
            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            double res = DotProd(vv1, vv2);

            return new SqlDouble(res);
        }

        public static double[] CrossProd(double[] vv1, double[] vv2)
        {
            double[] res = new double[3];

            if (vv1.Length != 3 || vv2.Length != 3)
                throw new Exception("Only valid for 3d vectors.");

            res[0] = vv1[1] * vv2[2] - vv1[2] * vv2[1];
            res[1] = vv1[2] * vv2[0] - vv1[0] * vv2[2];
            res[2] = vv1[0] * vv2[1] - vv1[1] * vv2[0];

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorCrossProd(SqlBinary v1, SqlBinary v2)
        {

            double[] vv1, vv2;
            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            double[] res = CrossProd(vv1, vv2);

            return new SqlBinary(Converters.VectorConverter.ToBinary(res));
        }

        public static double TripleProd(double[] v1, double[] v2, double[] v3)
        {
            double[][] mm = new double[3][];

            mm[0] = v1;
            mm[1] = v2;
            mm[2] = v3;

            Mapack.Matrix m = new Mapack.Matrix(mm);

            return m.Determinant;

        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlDouble VectorTripleProd(SqlBinary v1, SqlBinary v2, SqlBinary v3)
        {
            double[] vv1 = Converters.VectorConverter.ToArray(v1.Value);
            double[] vv2 = Converters.VectorConverter.ToArray(v2.Value);
            double[] vv3 = Converters.VectorConverter.ToArray(v3.Value);

            double res = TripleProd(vv1, vv2, vv3);
    
            return new SqlDouble(res);
        }

        public static double[][] TensorProd(double[] vv1, double[] vv2)
        {
            double[][] res = new double[3][];

            if (vv1.Length != 3 || vv2.Length != 3)
                throw new Exception("Only valid for 3d vectors.");

            for (int i = 0; i < res.Length; i++)
            {
                res[i] = new double[3];
                for (int j = 0; j < res[i].Length; j++)
                    res[i][j] = vv1[i] * vv2[j];
            }

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorTensorProd(SqlBinary v1, SqlBinary v2)
        {
            double[] vv1, vv2;
            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            double[][] res = TensorProd(vv1, vv2);

            return new SqlBinary(Converters.MatrixConverter.ToBinary(res));
        }

        public static double Dist2(double[] a, double[] b)
        {
            double dist = 0;
            for (int k = 0; k < a.Length; k++)
                dist += (a[k] - b[k]) * (a[k] - b[k]);

            return dist;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlSingle VectorDistance(SqlBinary v1, SqlBinary v2)
        {
            double[] vv1, vv2;

            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            double res = Dist2(vv1, vv2);

            return new SqlSingle(Math.Sqrt(res));
        }

        public static double[] Add(double[] vv1, double[] vv2)
        {
            double[] res = new double[vv1.Length];

            for (int i = 0; i < vv1.Length; i++)
                res[i] = vv1[i] + vv2[i];

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorAdd(SqlBinary v1, SqlBinary v2)
        {
            double[] vv1, vv2, res;

            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            res = Add(vv1, vv2);

            return new SqlBinary(Converters.VectorConverter.ToBinary(res));
        }

        public static double[] Subtract(double[] vv1, double[] vv2)
        {
            double[] res = new double[vv1.Length];

            for (int i = 0; i < vv1.Length; i++)
                res[i] = vv1[i] - vv2[i];

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorSubtract(SqlBinary v1, SqlBinary v2)
        {
            double[] vv1, vv2, res;

            vv1 = Converters.VectorConverter.ToArray(v1.Value);
            vv2 = Converters.VectorConverter.ToArray(v2.Value);

            res = Subtract(vv1, vv2);

            return new SqlBinary(Converters.VectorConverter.ToBinary(res));
        }

        public static double[] Mul(double c, double[] vv)
        {
            double[] res = new double[vv.Length];

            for (int i = 0; i < vv.Length; i++)
                res[i] = vv[i] * c;

            return res;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorMul(SqlDouble c, SqlBinary v)
        {
            double[] vv, res;

            vv = Converters.VectorConverter.ToArray(v.Value);

            res = Mul(c.Value, vv);

            return new SqlBinary(Converters.VectorConverter.ToBinary(res));
        }

        public static double[] Joggle(double[] v, double amount)
        {
            for (int i = 0; i < v.Length; i++)
                v[i] += amount * (rnd.NextDouble() * 2 - 1);

            return v;
        }

        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlBinary VectorJoggle(SqlBinary coord, SqlDouble amount)
        {
            double[] v = Elte.SqlSpIdx.Converters.VectorConverter.ToArray(coord.Value);

            return new SqlBinary(Elte.SqlSpIdx.Converters.VectorConverter.ToBinary(Joggle(v, amount.Value)));
        }

    }
}
